package server;

import contacts.Contact;
import contacts.ContactHelper;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;

import static com.google.common.base.Strings.isNullOrEmpty;
import static java.net.URLDecoder.decode;

/**
 * User: Jaan
 * Date: 4.12.11
 * Time: 0:32
 */
public class HttpRequestHandler implements org.apache.http.protocol.HttpRequestHandler {

  private static org.apache.log4j.Logger log = utils.Logger.getInstance(HttpRequestHandler.class.getName());
  ContactHelper contactHelper;
  Contact user;

  public HttpRequestHandler(Contact user) {
    super();
    this.user = user;
    contactHelper = new ContactHelper();
    contactHelper.setFilePathAndName("./contacts/" + user.getIPAndPort().replace(":", "_") + ".txt");
  }

  public void handle(
    final HttpRequest request,
    final HttpResponse response,
    final HttpContext context) throws HttpException, IOException {

    String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
    if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
      throw new MethodNotSupportedException(method + " method not supported");
    }

    if (request instanceof HttpEntityEnclosingRequest) {
      HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
      byte[] entityContent = EntityUtils.toByteArray(entity);
      System.out.println("Incoming entity content (bytes): " + entityContent.length);
    }
    // Lets process the request.
    processRequest(request, response);
  }


  public void processRequest(final HttpRequest request, final HttpResponse response) {
    String requestURI = request.getRequestLine().getUri();
    URIParser parser = new URIParser(request);
    Integer timeToLive;
    //We have to check ttl in order to filter the dead requests we should not process.
    try {
      timeToLive = Integer.parseInt(parser.getParameter("ttl"));
    } catch (Exception e) {
      log.warn("No TTL parameter found. Assuming the request is dead or irrelevant.");
      timeToLive = 0;
    }

    if (isValidRequest(timeToLive)) {
      if (requestURI.contains("findname")) {
        processFindNameRequest(request, response, "");
      } else if (requestURI.contains("sendmessage")) {
        processSendMessageRequest(request);
      } else if (requestURI.contains("asknames")) {
        processAskNamesResponse(response, processAskNamesRequest());
      } else if (requestURI.contains("sendname")) {
        processSendNameRequest(request);
      }
    } else {
      response.setStatusCode(HttpStatus.SC_OK);
    }
  }

  private void processSendNameRequest(HttpRequest request) {
    URIParser parser = new URIParser(request);
    String name = parser.getParameter("name");
    String ipAndPort = parser.getParameter("ip");
    Integer ttl = Integer.parseInt(parser.getParameter("ttl"));

    log.info("Sendname received " + name + " " + ipAndPort);
    contactHelper.loadContacts();
    ArrayList<Contact> contacts = contactHelper.getMyContacts();
    if (user.getIPAndPort().equals(ipAndPort) && user.getName().equals(name) && !user.isAlreadyFound()) {
      System.err.println("Choose another name, " + name + " is already in chat");
      user.setAlreadyFound(true);
      user.setName(null);
    } else {
      // Send names to other people also
      utils.HttpGet.forwardSendname(contacts, name, ipAndPort, ttl - 1);

      if (contactHelper.getContact(name) == null) {
        // Contact not in list, save it
        System.out.println("Good to know you received sendname, write contact to iptable");
        Contact newContact = new Contact();
        newContact.setName(name);
        newContact.setIpAndPort(ipAndPort);
        contactHelper.addContact(newContact);
      }
    }

  }

  private EntityTemplate processAskNamesResponse(HttpResponse response, final String result) {
    response.setStatusCode(HttpStatus.SC_OK);
    EntityTemplate body = new EntityTemplate(new ContentProducer() {

      public void writeTo(final OutputStream outstream) throws IOException {
        OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
        writer.write(result);
        writer.flush();
      }
    });
    body.setContentType("application/json; charset=UTF-8");
    response.setEntity(body);
    return body;
  }

  String processAskNamesRequest() {
    return contactHelper.getFileHelper().readFromFile(contactHelper.getFilePathAndName());
  }

  String processSendMessageRequest(HttpRequest request) {
    URIParser parser = new URIParser(request);
    //String sender = parser.getParameter("myname");
    //if (isNullOrEmpty(sender) || sender.equals("null")) {
    contactHelper.loadContacts();
    String senderIP = parser.getParameter("myip");
    String sender = contactHelper.getContactNameByIp(senderIP);
    if (sender == "Unknown") {
      sender = parser.getParameter("myname");
      Contact contact = new Contact(sender, senderIP);
      contactHelper.addContact(contact);
    }
    //}
    String message = decode(parser.getParameter("message"));
    DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    Date date = new Date();
    String composedMessage = dateFormat.format(date) + ": " + sender + ": " + message;
    System.out.println(composedMessage);
    return composedMessage;
  }

  void processFindNameRequest(HttpRequest request, HttpResponse response, final String result) {

    //Extract all the necessary parameters.
    URIParser parser = new URIParser(request);
    String name = parser.getParameter("name");
    String ipAndPort = parser.getParameter("myip");
    Integer timeToLive = Integer.parseInt(parser.getParameter("ttl"));

    contactHelper.loadContacts();
    if (!isNullOrEmpty(name) && !isNullOrEmpty(ipAndPort)) {
      Contact requestedContact = contactHelper.getContact(name);
      if (requestedContact != null && requestedContact.getName().equalsIgnoreCase(name)) {
        // If the contact is in our list            response.getParams()
        String sendnameRequestUrl = String.format(
          "http://%s/chat/sendname?name=%s&ip=%s&ttl=%d", ipAndPort, name, ipAndPort, 1);
        utils.HttpGet.execute(sendnameRequestUrl);
        //Make a response.
      } else {
        // Contact is not in list we have to send request forward
        ArrayList<Contact> contacts = contactHelper.getMyContacts();
        if (isValidList(contacts)) {
          for (Contact contact : contacts) {
            HttpClient httpclient = new DefaultHttpClient();
            String findnameRequestUrl = String.format(
              "http://%s/chat/findname?name=%s&myip=%s&ttl=%d", contact.getIPAndPort(), name, ipAndPort, timeToLive - 1);
//            System.out.println("Send namefind request> " + findnameRequestUrl);
            utils.HttpGet.execute(findnameRequestUrl);
          }
        }
      }
    }
    response.setStatusCode(HttpStatus.SC_OK);
  }

  public boolean isValidRequest(Integer timeToLive) {
    return timeToLive > 0;
  }

  public boolean isValidList(ArrayList list) {
    return list.size() > 0;
  }

}
